#include "PacketReader.h"

#include "XMPPConnection.h"
#include "PacketCollector.h"
#include "PacketListener.h"
#include "ConnectionListener.h"

#include <packet/IQ.h>
#include <packet/Presence.h>
#include <packet/Authentication.h>
#include <packet/RosterPacket.h>
#include <packet/Registration.h>
#include <packet/Bind.h>
#include <packet/Packet.h>

#include <filter/PacketFilter.h>

#include <util/xmlpull/XmlPullParser.h>
#include <util/PacketParserUtils.h>
#include <util/Utility.h>

#include <boost/bind.hpp>





PacketReader::PacketReader(XMPPConnection * connection) : 
	connection(connection), 
	done(false), 
	connectionID("")
{
    resetParser();
}

void PacketReader::init() {
}

PacketCollector * PacketReader::createPacketCollector(PacketFilter * packetFilter) {
    PacketCollector * collector = new PacketCollector(this, packetFilter);
    collectors.push_back(collector);
    // Add the collector to the list of active collector.
    return collector;
}

void PacketReader::cancelPacketCollector(PacketCollector * packetCollector) {
	std::vector<PacketCollector*>::iterator it = std::find(collectors.begin(), collectors.end(), packetCollector); 
	if(it != collectors.end()) {
		PacketCollector * p = *it;
		delete p;
		collectors.erase(it);
	}
}

void PacketReader::addPacketListener(PacketListener * packetListener, PacketFilter * packetFilter) {
	ListenerWrapper * wrapper = new ListenerWrapper(packetListener, packetFilter);
	listeners[packetListener] = wrapper;
}

void PacketReader::removePacketListener(PacketListener * packetListener) {
	std::map<PacketListener*, ListenerWrapper*>::iterator it = listeners.find(packetListener);
	if(it != listeners.end()) {
		PacketListener * p = it->first;
		ListenerWrapper * l = it->second;
		delete p; delete l;
		listeners.erase(it);
	}
}

void PacketReader::startup() {
//    connectionSemaphore = new Semaphore(1);

	boost::thread readerThread(boost::thread(boost::bind(&PacketReader::parsePackets, this)));
	readerThread.join();

    // A waiting thread may be woken up before the wait time or a notify
    // (although this is a rare thing). Therefore, we continue waiting
    // until either a connectionID has been set (and hence a notify was
    // made) or the total wait time has elapsed.
	/*
    int waitTime = SmackConfiguration::getPacketReplyTimeout();
    connectionSemaphore.tryAcquire(3 * waitTime, TimeUnit.MILLISECONDS);
	*/

    if (!connectionID.empty()) {
        connection->getConnectionID() = connectionID;
    }
}

void PacketReader::shutdown() {
    // Notify connection listeners of the connection closing if done hasn't already been set.
    if (!done) {
		/*
    	for (std::vector<ConnectionListener*>::iterator it = connectionListeners.begin(); it != connectionListeners.end(); ++it) {
    		it->connectionClosed();
    	}
		*/
		std::for_each(connectionListeners.begin(), connectionListeners.end(), boost::mem_fn(&ConnectionListener::connectionClosed));
    }
    done = true;

    // Shut down the listener executor.
    //TODO we should start a new thread for listener executor. listenerExecutor.shutdown();
}

void PacketReader::cleanup() {
	std::for_each(connectionListeners.begin(), connectionListeners.end(), DeleteObject());
	std::for_each(collectors.begin(), collectors.end(), DeleteObject());
	for(std::map<PacketListener*, ListenerWrapper*>::iterator it = listeners.begin(); it != listeners.end(); it++) {
		delete it->first; delete it->second;
	}
	connectionListeners.clear();
    listeners.clear();
    collectors.clear();
}

void PacketReader::notifyReconnection() {
    // Notify connection listeners of the reconnection.
	std::for_each(connectionListeners.begin(), connectionListeners.end(), boost::mem_fn(&ConnectionListener::reconnectionSuccessful));
}

void PacketReader::notifyConnectionError(std::string & err) {
	done = true;
	connection->shutdown(new Presence(Presence::TYPE_UNAVAILABLE));
	std::for_each(connectionListeners.begin(), connectionListeners.end(), boost::bind(boost::mem_fn(&ConnectionListener::connectionClosedOnError), _1, err));
}

bool PacketReader::hasConnectionListener(ConnectionListener * connectionListener) {
	return connectionListeners.find(connectionListener) != connectionListeners.end();
}

void PacketReader::addConnectionListener(ConnectionListener * connectionListener) {
	connectionListeners.insert(connectionListener);
}

void PacketReader::removeConnectionListener(ConnectionListener * connectionListener) {
	connectionListeners.erase(connectionListener);
}

void PacketReader::resetParser() {
    parser = new XmlPullParser(*(connection->getReader()));
    parser->setFeature(FEATURE_PROCESS_NAMESPACES, true);
}

void PacketReader::parsePackets() {
	int eventType = parser->getEventType();
	do {
		if (eventType == XmlPullParser::START_TAG) {
			PacketParser * packetParser = PacketParserFactory::get(parser->getName());
			if (packetParser == NULL) { continue; }
			packetParser->parse(parser);
			delete packetParser;
		} else if (eventType == XmlPullParser::END_TAG) {
            if (parser->getName() == "stream") {
                // Disconnect the connection
                connection->disconnect();
			}
        }
        eventType = parser->next();
    } while (!done && eventType != XmlPullParser::END_DOCUMENT);

}

void PacketReader::releaseConnectionIDLock() {
}


void PacketReader::processPacket(Packet * packet) {
}

void PacketReader::parseFeatures(XmlPullParser * parser) {
}

void PacketReader::setConnectionID(std::string id) {
	connectionID = id;
}


void PacketReader::ListenerWrapper::notifyListener(Packet * packet) {
	if (packetFilter == NULL || packetFilter->accept(packet)) {
   		packetListener->processPacket(packet);
    }
}


/*

void PacketReader::ListenerNotification::run(void * arg) {
	std::for_each(listeners.begin(), listeners.end(), boost::mem_fn(&PacketReader::ListenerWrapper::notifyListener));
}
*/

void PacketReader::MessagePacketParser::parse(XmlPullParser * parser) {
	packetReader->processPacket(PacketParserUtils::parseMessage(parser));	
}

void PacketReader::IQPacketParser::parse(XmlPullParser * parser) {
	packetReader->processPacket(PacketParserUtils::parsePresence(parser));	
}

void PacketReader::StreamPacketParser::parse(XmlPullParser * parser) {
	// Ensure the correct jabber:client namespace is being used.
	if (parser->getNamespace(std::string("")) == "jabber:client") {
		// Get the connection id.
		for (int i=0; i<parser->getAttributeCount(); i++) {
			if (parser->getAttributeName(i) == "id") {
				// Save the connectionID
				packetReader->setConnectionID(parser->getAttributeValue(i));
				if (parser->getAttributeValue(std::string(""), std::string("version")) != "1.0") {
					// Notify that a stream has been opened if the
					// server is not XMPP 1.0 compliant otherwise make the
					// notification after TLS has been negotiated or if TLS
					// is not supported
					packetReader->releaseConnectionIDLock();
				}
			}
			else if (parser->getAttributeName(i) == "from") {
				// Use the server name that the server says that it is.
				packetReader->connection->setServiceName(parser->getAttributeValue(i));
			}
		}
    }
}

void PacketReader::FeaturePacketParser::parse(XmlPullParser * parser) {
	packetReader->parseFeatures(parser);
}



PacketReader::PacketParserFactory PacketReader::PacketParserFactory::_instance;
